﻿/*              Copyright(c) 2008 Dave Sexton              *
 * http://www.codeplex.com/DocProject                      *
 * http://www.codeplex.com/DocProject/Project/License.aspx *
 ***********************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace DaveSexton.DocToMaml
{
	internal sealed class ConsoleContext : IDisposable
	{
		#region Public Properties
		public bool IsAttachedToParent
		{
			get
			{
				return isAttachedToParent;
			}
		}
		#endregion

		#region Private / Protected
		private const int ERROR_INVALID_HANDLE = 6, ATTACH_PARENT_PROCESS = -1;
		private readonly bool isAttachedToParent;
		#endregion

		#region Constructors
		/// <summary>
		/// Constructs a new instance of the <see cref="ConsoleContext" /> class.
		/// </summary>
		public ConsoleContext(bool canAttachToParent)
		{
			int lastError = 0;

			/* NOTE: AttachConsole doesn't appear to work in certain scenarios; it returns TRUE but Console.WriteLine doesn't output anything.
			 * 
			 * 1. Tested on Vista Business edition by starting the Visual Studio 2008 Command Prompt and entering: DocToMaml.exe -?
			 * 2. Tested on Vista Business edition by starting cmd.exe and entering: DocToMaml.exe -?
			 * 
			 * Test 1 doesn't output anything.
			 * Test 2 behaves correctly.
			 * 
			 * The workaround is to accept a parameter that indicates whether AttachConsole should be called so that the user can decide if 
			 * attaching to the parent console is appropriate.
			 */
			if (!canAttachToParent || (!NativeMethods.AttachConsole(ATTACH_PARENT_PROCESS) && (lastError = Marshal.GetLastWin32Error()) == ERROR_INVALID_HANDLE))
			{
				if (!NativeMethods.AllocConsole())
					lastError = Marshal.GetLastWin32Error();
			}
			else if (lastError == 0)
				isAttachedToParent = true;
			
			if (lastError != 0)
				Marshal.ThrowExceptionForHR(lastError);
		}
		#endregion

		#region Methods
		#endregion

		#region IDisposable Members
		/// <summary>
		/// Releases all resources used by an instance of the <see cref="ConsoleContext" /> class.
		/// </summary>
		/// <remarks>
		/// This method calls the virtual <see cref="Dispose(bool)" /> method, passing in <strong>true</strong>, and then suppresses 
		/// finalization of the instance.
		/// </remarks>
		public void Dispose()
		{
			Dispose(true);
			GC.SuppressFinalize(this);
		}

		/// <summary>
		/// Releases unmanaged resources before an instance of the <see cref="ConsoleContext" /> class is reclaimed by garbage collection.
		/// </summary>
		/// <remarks>
		/// This method releases unmanaged resources by calling the virtual <see cref="Dispose(bool)" /> method, passing in <strong>false</strong>.
		/// </remarks>
		~ConsoleContext()
		{
			Dispose(false);
		}

		/// <summary>
		/// Releases the unmanaged resources used by an instance of the <see cref="ConsoleContext" /> class and optionally releases the managed resources.
		/// </summary>
		/// <param name="disposing"><strong>true</strong> to release both managed and unmanaged resources; <strong>false</strong> to release only unmanaged resources.</param>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "disposing")]
		private static void Dispose(bool disposing)
		{
			NativeMethods.FreeConsole();
		}
		#endregion
	}
}
